home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Windows Expert
/
Windows Expert.iso
/
windownt
/
uniput.zip
/
DISPLAY.C
next >
Wrap
C/C++ Source or Header
|
1992-10-26
|
22KB
|
761 lines
/**************************************************************************\
* display.c -- module to support the main MDI child windows.
*
* Steve Firebaugh
* Microsoft Developer Support
* Copyright (c) 1992 Microsoft Corporation
*
* window class registered in uniput.c
* windows are created in main frame window procedure.
*
* design: We have one window procedure here for potentially multiple
* MDI child windows. The nCharPerLine, SqrHeight, and SqrWidth are
* stored on a per window basis. Also, the window class is registered
* with the style CS_OWNDC. For this reason, it is possible to select
* a logical font into the HDC, and it will be there each time you get
* the DC new for the window. In this way we have a log font for each
* window without actually doing any work in this module.
*
* Some of the data is global, and works for all of the windows. The
* startcount & endcount arrays are a good example. These are computed
* once when the font is created, but the values remain the same for each
* window. Thus we have the following assumption:
*
* 1. No fonts will be installed or removed while program is running.
* (CountUCSegments values remain valid for run time duration).
*
*
* There are WM_USER+ messages to set values for this window, and to
* notify it of global changes. Looking at the create time, user message
* stream is helpful. On the WM_CREATE message, we send ourselves a
* WMU_NEWFONT message to create a logical font and count the number of
* character ranges (segments). Before returning from this message, we
* send ourselves a WMU_NEWRANGE message to set the title, and the
* horizontal scroll bar correctly.
*
*
\**************************************************************************/
#define UNICODE
#include <windows.h>
#include "uniput.h"
/* global variables store the start and end codepoints for UC ranges. */
#define MAXSEGMENTS 100
USHORT endCount[MAXSEGMENTS];
USHORT startCount[MAXSEGMENTS];
int CountUCSegments (HDC);
/* error return value from CountUCSegments */
#define SEGMENTERROR -1
/* conversion between (x,y) pairs and rectangle index */
int transXYtoIndex (int, int, int, int, int);
int transIndextoRect (int, PRECT, int, int, int);
/* window extra bytes for use to store window specific data (see register class) */
#define GWLU_NCHAR 0
#define GWLU_SQRHEIGHT 4
#define GWLU_SQRWIDTH 8
/* structure for the character range 'name' lookup table. */
typedef struct tagLookupEntry{
USHORT start;
USHORT end;
TCHAR String[32];
} LookupEntry;
/* The following data comes straight out of the Addison-Wesley Unicode book. */
#define NRANGE 24
LookupEntry RangeName[NRANGE] =
{{ 0x0000,0x007f, TEXT("ASCII")},
{ 0x0080,0x00ff, TEXT("Latin1 Characters")},
{ 0x0100,0x017f, TEXT("European Latin")},
{ 0x0180,0x01ff, TEXT("Extended Latin")},
{ 0x0200,0x024f, TEXT("<Bad unicode range.>")},
{ 0x0250,0x02af, TEXT("Standard Phonetic")},
{ 0x02b0,0x02ff, TEXT("Modifier Letters")},
{ 0x0300,0x036f, TEXT("Generic Diacritical Marks")},
{ 0x0370,0x03ff, TEXT("Greek")},
{ 0x0400,0x04ff, TEXT("Cyrillic")},
{ 0x0500,0x052f, TEXT("<Bad unicode range.>")},
{ 0x0530,0x058f, TEXT("Armenian")},
{ 0x0590,0x05ff, TEXT("Hebrew")},
{ 0x0600,0x06ff, TEXT("Arabic")},
{ 0x0900,0x1fff, TEXT("<not specified in table.>")},
{ 0x2000,0x206f, TEXT("General Punctutation")},
{ 0x2070,0x209f, TEXT("Superscipts & Subscripts")},
{ 0x20a0,0x20cf, TEXT("Currency Symbols")},
{ 0x20d0,0x20ff, TEXT("Diacritical Marks for Symbols")},
{ 0x2100,0x214f, TEXT("Letterlike Symbols")},
{ 0x2150,0x218f, TEXT("Number Forms")},
{ 0x2190,0x21ff, TEXT("Arrows")},
{ 0x2200,0x22ff, TEXT("Mathematical Operators")},
{ 0x2300,0xffff, TEXT("<not specified in table.>")}};
/* Global logfont. Used for CreateFontIndirect().
* also regerenced in the uniput.c file.
*/
LOGFONT logfont = {
UCFONTHEIGHT ,
UCFONTWIDTH ,
0 ,
0 ,
400 ,
0 ,
0 ,
0 ,
UNICODE_CHARSET ,
0 ,
0 ,
2 ,
2 ,
TEXT("Lucida Sans Unicode")};
/**************************************************************************\
*
* function: DisplayWndProc()
*
* input parameters: normal window procedure parameters.
*
\**************************************************************************/
LRESULT CALLBACK DisplayWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HFONT hfont, hfontOld;
static HDC hdc;
static RECT rect;
static int iSeg;
static TCHAR buffer[100];
/* rDown and index used as the user points to a square. */
static RECT rDown;
static int index;
switch (message) {
/**********************************************************************\
* WM_CREATE
*
* Create font.
\**********************************************************************/
case WM_CREATE:
SendMessage (hwnd, WMU_SETNCHAR, 16, 0);
SendMessage (hwnd, WMU_NEWFONT, (WPARAM) logfont.lfWidth, (LPARAM) logfont.lfHeight);
break;
/**********************************************************************\
* WM_DESTROY
*
* Get rid of the logical font we create for each display window.
* relying here on CS_ONWDC
\**********************************************************************/
case WM_DESTROY:
hdc = GetDC (hwnd);
hfont = SelectObject (hdc, GetStockObject(SYSTEM_FONT));
ReleaseDC (hwnd, hdc);
DeleteObject (hfont);
break;
/**********************************************************************\
* WMU_SETNCHAR
*
* wParam - nCharPerLine
*
\**********************************************************************/
case WMU_SETNCHAR:
SetWindowLong (hwnd, GWLU_NCHAR, (int) wParam);
InvalidateRect (hwnd, NULL, TRUE);
break;
/**********************************************************************\
* WMU_NEWFONT
*
* wParam - Width
* lParam - Height
*
* Create font, select it into the HDC, and reset ranges.
\**********************************************************************/
case WMU_NEWFONT: {
int nSegments;
hdc = GetDC(hwnd);
logfont.lfHeight = (int) lParam;
logfont.lfWidth = (int) wParam;
hfont = CreateFontIndirect (&logfont);
hfontOld = SelectObject (hdc, hfont);
DeleteObject (hfontOld);
nSegments = CountUCSegments (hdc); /* slow computation */
ReleaseDC (hwnd, hdc);
/* verify that we have SOME ranges to work with */
if (nSegments == SEGMENTERROR) return FALSE;
/* warn the user if we can't find the right font */
if (nSegments < 30)
MessageBox (hwnd,
TEXT("Working w/ limited codepoint coverage."),
TEXT("Lucida Sans Unicode font not found."), MB_OK);
SetScrollRange (hwnd, SB_HORZ, 0, (nSegments-1), TRUE);
SetScrollPos (hwnd, SB_HORZ, 0, TRUE);
SendMessage (hwnd, WMU_NEWRANGE, 0,0);
} return TRUE;
/**********************************************************************\
* WMU_NEWRANGE
*
*
* Range changes, or font size changes, or title status changes.
\**********************************************************************/
case WMU_NEWRANGE: {
int sqrHeight, sqrWidth;
TEXTMETRIC tm;
int i;
hdc = GetDC(hwnd);
GetTextMetrics (hdc, &tm);
ReleaseDC (hwnd, hdc);
/* index into the start, end arrays stored in scroll bar */
iSeg = GetScrollPos (hwnd, SB_HORZ);
/* Set the window title text to show display range. */
wsprintf (buffer, TEXT("[%04x, %04x]"),
(int)startCount[iSeg],
(int)endCount[iSeg]);
/* if we are supposed to look up the character range name,
* then step through the ranges stored in the RangeName
* table. Find the correct one, and reset the buffer string.
*/
if (gShowNames) {
for (i = 0; i< NRANGE; i++) {
if ((RangeName[i].start <= startCount[iSeg]) &&
( startCount[iSeg] <= RangeName[i].end))
wsprintf (buffer, TEXT("%s [%04x, %04x]"),
RangeName[i].String,
(int)startCount[iSeg],
(int)endCount[iSeg]);
}
}
SetWindowText (hwnd, buffer);
sqrHeight = tm.tmHeight + tm.tmExternalLeading;
sqrWidth = tm.tmMaxCharWidth;
SetWindowLong (hwnd, GWLU_SQRHEIGHT,sqrHeight);
SetWindowLong (hwnd, GWLU_SQRWIDTH, sqrWidth );
} return TRUE;
/**********************************************************************\
\**********************************************************************/
case WM_LBUTTONDOWN: {
int x,y;
int nCharPerLine;
int sqrHeight, sqrWidth;
nCharPerLine = GetWindowLong (hwnd, GWLU_NCHAR);
sqrHeight = GetWindowLong (hwnd, GWLU_SQRHEIGHT);
sqrWidth = GetWindowLong (hwnd, GWLU_SQRWIDTH );
x = (int)LOWORD (lParam);
y = (int)HIWORD (lParam);
index = transXYtoIndex (x,y, sqrWidth, sqrHeight, nCharPerLine);
/* verify that the index is within the shown segment range */
iSeg= GetScrollPos (hwnd, SB_HORZ);
if (index > (endCount[iSeg]- startCount[iSeg])) return NULL;
transIndextoRect (index, &rDown, sqrWidth, sqrHeight, nCharPerLine);
hdc = GetDC (hwnd);
InvertRect (hdc, &rDown);
ReleaseDC (hwnd, hdc);
GdiFlush ();
SetCapture (hwnd);
} break;
/**********************************************************************\
*
* rDown & index set in WM_LBUTTONDOWN
*
\**********************************************************************/
case WM_LBUTTONUP: {
POINT p;
if (GetCapture() == hwnd) {
p.x = (int)LOWORD (lParam);
p.y = (int)HIWORD (lParam);
if (PtInRect (&rDown, p)) {
iSeg= GetScrollPos (hwnd, SB_HORZ);
index += startCount[iSeg];
Beep (40,40);
SendMessage (hwndMain, WMU_CHARACTER, 0, (LPARAM) index);
}
hdc = GetDC (hwnd);
InvertRect (hdc, &rDown);
ReleaseDC (hwnd, hdc);
GdiFlush ();
ReleaseCapture ();
}
} break;
/**********************************************************************\
* WM_HSCROLL
*
* Step through the character ranges.
* In every case, inform the window the range has changed,
* and invalidate it to force a repaint.
\**********************************************************************/
case WM_HSCROLL:
switch (LOWORD(wParam)){
int OldPos, NewPos;
case SB_PAGEDOWN:
case SB_LINEDOWN:
OldPos = GetScrollPos (hwnd, SB_HORZ);
SetScrollPos (hwnd, SB_HORZ, (OldPos+1), TRUE);
SendMessage (hwnd,WMU_NEWRANGE, 0,0);
InvalidateRect (hwnd, NULL, TRUE);
break;
case SB_PAGEUP:
case SB_LINEUP:
OldPos = GetScrollPos (hwnd, SB_HORZ);
SetScrollPos (hwnd, SB_HORZ, (OldPos-1), TRUE);
SendMessage (hwnd,WMU_NEWRANGE, 0,0);
InvalidateRect (hwnd, NULL, TRUE);
break;
case SB_THUMBPOSITION:
OldPos = GetScrollPos (hwnd, SB_HORZ);
NewPos = HIWORD (wParam);
SetScrollPos (hwnd, SB_HORZ, NewPos, TRUE);
SendMessage (hwnd,WMU_NEWRANGE, 0,0);
InvalidateRect (hwnd, NULL, TRUE);
break;
}
break;
/**********************************************************************\
* WM_SIZE
*
\**********************************************************************/
case WM_SIZE:
/* make sure that scroll metrics are ok */
SendMessage (hwnd, WMU_NEWRANGE, 0,0);
break; /* fall through to MDIChildProc */
/**********************************************************************\
* WM_PAINT
*
\**********************************************************************/
case WM_PAINT: {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
POINT point;
int i;
USHORT start, end;
WCHAR outChar;
USHORT codepoint;
int nCharPerLine;
int sqrHeight, sqrWidth;
nCharPerLine = GetWindowLong (hwnd, GWLU_NCHAR);
sqrHeight = GetWindowLong (hwnd, GWLU_SQRHEIGHT);
sqrWidth = GetWindowLong (hwnd, GWLU_SQRWIDTH );
hdc = BeginPaint(hwnd, &ps);
SetBkMode (hdc, TRANSPARENT);
iSeg= GetScrollPos (hwnd, SB_HORZ);
start = startCount[iSeg];
end = endCount[iSeg];
/* ensure that we are in a valid range... some fonts have problems */
if (start != 0xffff)
for (codepoint = start, i=0; codepoint<=end; codepoint++,i++) {
/* paint box and frame it */
transIndextoRect (i, &rect, sqrWidth, sqrHeight, nCharPerLine);
InflateRect (&rect, -1, -1);
FillRect (hdc, &rect, GetStockObject (LTGRAY_BRUSH));
InflateRect (&rect, 1, 1);
FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
InflateRect (&rect, -1, -1);
SelectObject (hdc, GetStockObject (WHITE_PEN));
MoveToEx (hdc, rect.right, rect.top, NULL);
LineTo (hdc,rect.left, rect.top);
LineTo (hdc,rect.left, rect.bottom);
/* set point that we will draw glyph at */
point.x = (rect.right + rect.left)/2;
point.y = rect.top;
SetTextAlign (hdc, TA_CENTER | TA_TOP);
SetTextColor (hdc, PALETTEINDEX (0));
/* special case the non-spacing diacritic marks. U+0300 -> U+036F
* Write a space first, for them to 'modify.'
*/
if ( (0x0300 <= codepoint) && (codepoint <= 0x036F) ) {
outChar = (WCHAR) 0x0020;
TextOutW (hdc, 0,0, &outChar, 1);
}
outChar = (WCHAR) codepoint;
ExtTextOut(hdc, point.x, point.y, ETO_CLIPPED, &rect, (LPCTSTR)&outChar, 1, NULL);
/* fill in unicode code point in hex */
if (gShowhex) {
int nchar;
HANDLE hfonttemp;
nchar = wsprintf (buffer, TEXT("%04x"), (int) codepoint);
hfonttemp = SelectObject (hdc,GetStockObject (SYSTEM_FIXED_FONT));
point.y = rect.bottom;
SetTextAlign (hdc, TA_CENTER | TA_BOTTOM);
SetTextColor (hdc, PALETTEINDEX (5));
TextOut( hdc, point.x, point.y,
buffer, nchar);
SelectObject (hdc,hfonttemp);
}
} /* end for */
EndPaint (hwnd, &ps);
} return FALSE; /* end WM_PAINT */
} /* end switch */
return (DefMDIChildProc(hwnd, message, wParam, lParam));
}
/**************************************************************************\
*
* Need a mapping between the (x,y) pair, and the index of the square on
* the window. Two routines provide this... one for each direction.
*
\**************************************************************************/
/**********************************************************************\
* transXYtoIndex
*
* Given x & y values, return the index.
*
\**********************************************************************/
int transXYtoIndex (int x, int y,
int sqrWidth, int sqrHeight, int nCharPerLine)
{
x /= sqrWidth;
if (x < 0) x = 0;
if (x >= nCharPerLine) x = nCharPerLine-1;
y /= sqrHeight;
if (y < 0) y = 0;
return ( (y * nCharPerLine) + x);
}
/**********************************************************************\
* transIndextoRect
*
* Given an index, i.e. the number of one of the squares on the display
* window, fill in the rectangle structure.
*
\**********************************************************************/
int transIndextoRect (int index, PRECT pr,
int sqrWidth, int sqrHeight, int nCharPerLine)
{
int x,y;
x = index % nCharPerLine;
y = index / nCharPerLine;
pr->left = x*sqrWidth;
pr->top = y*sqrHeight;
pr->right = pr->left +sqrWidth;
pr->bottom = pr->top +sqrHeight;
return TRUE;
}
/**************************************************************************\
*
* All of the code below is used for parsing 'font data.' It will only
* make sense if you have a copy of the True Type font spec. In short,
* we grab the 'cmap' table, look through it for a subtable, and then
* get two parallel arrays from the subtable. Complications arise because
* the true type data is 'BIG ENDIAN' and NT is being run 'little endian.'
* For this reason, once we locate the short or long, we call Swap* to
* change the byte ordering.
*
\**************************************************************************/
VOID SwapShort (PUSHORT);
VOID SwapULong (PULONG);
typedef struct tagTABLE{
USHORT platformID;
USHORT encodingID;
ULONG offset;
} TABLE, *PTABLE;
typedef struct tagSUBTABLE{
USHORT format;
USHORT length;
USHORT version;
USHORT segCountX2;
USHORT searchRange;
USHORT entrySelector;
USHORT rangeShift;
} SUBTABLE, *PSUBTABLE;
/* 'cmap' is passed in by value in a DWORD */
#define CMAPHEX 0x636d6170
#define NBYTES 256
#define OFFSETERROR 0
/**************************************************************************\
*
* function: CountUCSegments()
*
* input parameters:
* hdc - with the logical font set into it.
* prect - pointer to client rectangle.
*
* Global variables:
* startCount
* endCount
*
* returns:
* number of UC segments or
* SEGMENTERROR if there is some kind of error.
*
* essential side effect:
* Fills in global startCount, endCount arrays.
\**************************************************************************/
int CountUCSegments (HDC hdc)
{
DWORD cbData;
USHORT aShort[2];
DWORD nBytes;
USHORT i, nTables;
PTABLE pTable;
PSUBTABLE pSubTable;
ULONG offset,offsetFormat4;
USHORT segCount;
BYTE buffer[NBYTES];
/* find number of 'subtables', second long in cmap */
nBytes=GetFontData (hdc, CMAPHEX, 0, aShort, 4);
if (nBytes == (DWORD)-1) {
MessageBox (NULL, MBGETFONTDATAERR,MBERROR , MBERRORFLAGS);
return SEGMENTERROR;
}
nTables = aShort[1];
SwapShort (&nTables);
cbData = nTables * sizeof(TABLE);
if (cbData >NBYTES) {
MessageBox (NULL, TEXT("cbData >NBYTES"),MBERROR , MBERRORFLAGS);
return SEGMENTERROR;
}
/* get array of subtables information. Check each one for 3,1*/
nBytes=GetFontData (hdc, CMAPHEX, 4, buffer, cbData);
pTable = (PTABLE)buffer;
offsetFormat4 = OFFSETERROR;
for (i = 0; i< nTables; i++) {
SwapShort (&(pTable->encodingID));
SwapShort (&(pTable->platformID));
if ((pTable->platformID == 3)&&(pTable->encodingID == 1)) {
offsetFormat4 = pTable->offset;
SwapULong (&offsetFormat4);
break;
}
pTable++;
}
/* verify that we got the correct offset to the FORMAT 4 subtable */
if (offsetFormat4 == OFFSETERROR){
MessageBox (NULL, TEXT("Can not find 3,1 subtable"),MBERROR , MBERRORFLAGS);
return SEGMENTERROR;
}
/* Get the beginning of the subtable, especially the segment count */
nBytes=GetFontData (hdc, CMAPHEX, offsetFormat4, buffer, sizeof(SUBTABLE));
pSubTable = (PSUBTABLE) buffer;
SwapShort (&(pSubTable->format));
SwapShort (&(pSubTable->segCountX2));
if (pSubTable->format != 4){
MessageBox (NULL, TEXT("format !=4"), MBERROR, MBERRORFLAGS);
return SEGMENTERROR;
}
segCount = pSubTable->segCountX2 / 2;
if (segCount > MAXSEGMENTS){
MessageBox (NULL, TEXT("segCount > MAXSEGMENTS"), MBERROR, MBERRORFLAGS);
return SEGMENTERROR;
}
/* read in the array of endCount values */
offset = offsetFormat4
+ (7 * sizeof (USHORT)); /* skip constant # bytes in subtable */
cbData = segCount * sizeof (USHORT);
nBytes=GetFontData (hdc, CMAPHEX, offset, endCount, cbData );
for (i = 0; i<segCount; i++)
SwapShort (& (endCount[i]));
/* read in the array of startCount values */
offset = offsetFormat4
+ (7 * sizeof (USHORT)) /* skip constant # bytes in subtable */
+ (segCount * sizeof (USHORT)) /* skip endCount array */
+ sizeof (USHORT); /* skip reservedPad */
cbData = segCount * sizeof (USHORT);
nBytes=GetFontData (hdc, CMAPHEX, offset, startCount, cbData );
for (i = 0; i<segCount; i++)
SwapShort (& (startCount[i]));
return segCount;
}
VOID SwapShort (PUSHORT p)
{
SHORT temp;
temp =(SHORT)( HIBYTE (*p) + (LOBYTE(*p) << 8));
*p = temp;
}
VOID SwapULong (PULONG p)
{
ULONG temp;
temp = (LONG) ((BYTE) *p);
temp <<= 8;
*p >>=8;
temp += (LONG) ((BYTE) *p);
temp <<= 8;
*p >>=8;
temp += (LONG) ((BYTE) *p);
temp <<= 8;
*p >>=8;
temp += (LONG) ((BYTE) *p);
*p = temp;
}